checksum: implement conversion from string and to base64
authorFelix Krull <f_krull@gmx.de>
Mon, 2 Sep 2019 13:56:52 +0000 (15:56 +0200)
committerColin Walters <walters@verbum.org>
Fri, 6 May 2022 16:53:54 +0000 (12:53 -0400)
rust-bindings/rust/src/checksum.rs
rust-bindings/rust/src/collection_ref.rs

index b2f483073e2e250a67ba56880a3cfcdd03a4ac00..2dc250310e3e839189d79f5e6494b60e24d5c605 100644 (file)
@@ -1,21 +1,64 @@
-use glib::translate::{from_glib_full, FromGlibPtrFull};
+use glib::translate::{from_glib_full, FromGlibPtrFull, ToGlibPtr};
 use glib::GString;
 use glib_sys::{g_free, gpointer};
 use std::fmt;
 
+const BYTES_BUF_SIZE: usize = ostree_sys::OSTREE_SHA256_DIGEST_LEN as usize;
+const HEX_BUF_SIZE: usize = (ostree_sys::OSTREE_SHA256_STRING_LEN + 1) as usize;
+const B64_BUF_SIZE: usize = 44;
+
+/// A binary SHA256 checksum.
+#[derive(Debug)]
 pub struct Checksum {
-    bytes: *mut [u8; 32],
+    bytes: *mut [u8; BYTES_BUF_SIZE],
 }
 
 impl Checksum {
-    pub(crate) unsafe fn new(bytes: *mut [u8; 32]) -> Checksum {
+    /// Create a `Checksum` value, taking ownership of the given memory location.
+    ///
+    /// # Safety
+    /// `bytes` must point to a fully initialized 32-byte memory location that is freeable with
+    /// `g_free` (this is e.g. the case if the memory was allocated with `g_malloc`). The value
+    /// takes ownership of the memory, i.e. the memory is freed when the value is dropped. The
+    /// memory must not be freed by other code.
+    unsafe fn new(bytes: *mut [u8; BYTES_BUF_SIZE]) -> Checksum {
         assert!(!bytes.is_null());
         Checksum { bytes }
     }
 
+    /// Create a `Checksum` from a hexadecimal SHA256 string.
+    ///
+    /// Unfortunately, the underlying libostree function has no way to report parsing errors. If the
+    /// string is not a valid SHA256 string, the program will abort!
+    // TODO: implement by hand to avoid stupid assertions?
+    pub fn from_string(checksum: &str) -> Checksum {
+        unsafe {
+            from_glib_full(ostree_sys::ostree_checksum_to_bytes(
+                checksum.to_glib_none().0,
+            ))
+        }
+    }
+
+    /// Return checksum as a hex digest string.
     fn to_gstring(&self) -> GString {
         unsafe { from_glib_full(ostree_sys::ostree_checksum_from_bytes(self.bytes)) }
     }
+
+    /// Convert checksum to base64.
+    pub fn to_base64(&self) -> String {
+        let mut buf: Vec<u8> = Vec::with_capacity(B64_BUF_SIZE);
+        unsafe {
+            ostree_sys::ostree_checksum_b64_inplace_from_bytes(
+                self.bytes,
+                buf.as_mut_ptr() as *mut i8,
+            );
+            let len = libc::strlen(buf.as_mut_ptr() as *const i8);
+            // Assumption: (len + 1) valid bytes are in the buffer.
+            buf.set_len(len);
+            // Assumption: all characters are ASCII, ergo valid UTF-8.
+            String::from_utf8_unchecked(buf)
+        }
+    }
 }
 
 impl Drop for Checksum {
@@ -26,21 +69,21 @@ impl Drop for Checksum {
     }
 }
 
-impl FromGlibPtrFull<*mut [u8; 32]> for Checksum {
-    unsafe fn from_glib_full(ptr: *mut [u8; 32]) -> Self {
+impl FromGlibPtrFull<*mut [u8; BYTES_BUF_SIZE]> for Checksum {
+    unsafe fn from_glib_full(ptr: *mut [u8; BYTES_BUF_SIZE]) -> Self {
         Checksum::new(ptr)
     }
 }
 
-impl FromGlibPtrFull<*mut [*mut u8; 32]> for Checksum {
-    unsafe fn from_glib_full(ptr: *mut [*mut u8; 32]) -> Self {
-        Checksum::new(ptr as *mut u8 as *mut [u8; 32])
+impl FromGlibPtrFull<*mut [*mut u8; BYTES_BUF_SIZE]> for Checksum {
+    unsafe fn from_glib_full(ptr: *mut [*mut u8; BYTES_BUF_SIZE]) -> Self {
+        Checksum::new(ptr as *mut u8 as *mut [u8; BYTES_BUF_SIZE])
     }
 }
 
 impl FromGlibPtrFull<*mut u8> for Checksum {
     unsafe fn from_glib_full(ptr: *mut u8) -> Self {
-        Checksum::new(ptr as *mut [u8; 32])
+        Checksum::new(ptr as *mut [u8; BYTES_BUF_SIZE])
     }
 }
 
@@ -49,3 +92,34 @@ impl fmt::Display for Checksum {
         write!(f, "{}", self.to_gstring())
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use glib_sys::g_malloc0;
+
+    const CHECKSUM_STRING: &str =
+        "bf875306783efdc5bcab37ea10b6ca4e9b6aea8b94580d0ca94af120565c0e8a";
+
+    #[test]
+    fn should_create_checksum_from_bytes() {
+        let bytes = unsafe { g_malloc0(BYTES_BUF_SIZE) } as *mut u8;
+        let checksum: Checksum = unsafe { from_glib_full(bytes) };
+        assert_eq!(checksum.to_string(), "00".repeat(BYTES_BUF_SIZE));
+    }
+
+    #[test]
+    fn should_parse_checksum_string_to_bytes() {
+        let csum = Checksum::from_string(CHECKSUM_STRING);
+        assert_eq!(csum.to_string(), CHECKSUM_STRING);
+    }
+
+    #[test]
+    fn should_convert_checksum_to_base64() {
+        let csum = Checksum::from_string(CHECKSUM_STRING);
+        assert_eq!(
+            csum.to_base64(),
+            "v4dTBng+_cW8qzfqELbKTptq6ouUWA0MqUrxIFZcDoo"
+        );
+    }
+}
index 428c0b569b1330c48e432ede8e4a097567afe0dd..b85a3f4c72e90d1c7a671ce13e6c3fbefd38b425 100644 (file)
@@ -19,8 +19,6 @@ impl<T> AsNonnullPtr for *mut T {
     }
 }
 
-// TODO: these methods are unsound if you change the underlying OstreeCollectionRef from C. Is that
-//  a problem?
 impl CollectionRef {
     /// Get the collection ID from this `CollectionRef`.
     ///